package redisext

import (
	"context"
	"fmt"
	"time"

	"gitee.com/xfrm/middleware/xcache"
	"gitee.com/xfrm/middleware/xcache/constants"
	"gitee.com/xfrm/middleware/xcache/redis"
	"gitee.com/xfrm/middleware/xcontext"
	"gitee.com/xfrm/middleware/xtime"
	"gitee.com/xfrm/middleware/xtrace"

	redis2 "github.com/go-redis/redis"
)

type RedisExt struct {
	namespace string
	prefix    string
	noFixKey  bool // redis client no fix key
	cluster   string
}

func NewRedisExt(namespace, prefix string) *RedisExt {
	return &RedisExt{
		namespace: namespace,
		prefix:    prefix}
}

// NewRedisExtNoPrefix not fix redis key
func NewRedisExtNoPrefix(namespace string) *RedisExt {
	return &RedisExt{
		namespace: namespace,
		noFixKey:  true}
}

type Z struct {
	Score  float64
	Member interface{}
}

func (z Z) toRedisZ() redis2.Z {
	return redis2.Z{
		Score:  z.Score,
		Member: z.Member,
	}
}

func fromRedisZ(rz redis2.Z) Z {
	return Z{
		Score:  rz.Score,
		Member: rz.Member,
	}
}

func toRedisZSlice(zs []Z) (rzs []redis2.Z) {
	for _, z := range zs {
		rzs = append(rzs, z.toRedisZ())
	}
	return
}

func fromRedisZSlice(rzs []redis2.Z) (zs []Z) {
	for _, rz := range rzs {
		zs = append(zs, fromRedisZ(rz))
	}
	return
}

type ZRangeBy struct {
	Min, Max      string
	Offset, Count int64
}

func toRedisZRangeBy(by ZRangeBy) redis2.ZRangeBy {
	return redis2.ZRangeBy{
		Min:    by.Min,
		Max:    by.Max,
		Offset: by.Offset,
		Count:  by.Count,
	}
}

func (m *RedisExt) prefixKey(key string) string {
	if len(m.prefix) > 0 {
		key = fmt.Sprintf("%s.%s", m.prefix, key)
	}
	return key
}

func (m *RedisExt) getRedisInstance(ctx context.Context) (client *redis.Client, err error) {
	conf := m.getInstanceConf(ctx)
	client, err = redis.DefaultInstanceManager.GetInstance(ctx, conf)
	if client == nil {
		return
	}

	m.cluster = client.Cluster()
	return
}

func (m *RedisExt) getInstanceConf(ctx context.Context) *redis.InstanceConf {
	return &redis.InstanceConf{
		Group:     xcontext.GetControlRouteGroupWithDefault(ctx, constants.DefaultRouteGroup),
		Namespace: m.namespace,
		Wrapper:   xcache.WrapperTypeRedisExt,
		NoFixKey:  m.noFixKey,
	}
}

/*  注意： 不支持mset、msetnx命令；
因为即使在这里实现，也需要调用方区别于其他命令，不如直接调用mset接口。
因为需要把key包装一下，所以这里把cmd、key拆出来。
命令的拼接逻辑是cmd、 keys、otherArgs按顺序拼接。*/
func (m *RedisExt) Do(ctx context.Context, cmd string, keys []string, otherArgs ...interface{}) (s interface{}, err error) {
	command := "redisext.Do"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		var prefixKey []string
		for _, v := range keys {
			prefixKey = append(prefixKey, m.prefixKey(v))
		}
		s, err = client.Do(ctx, cmd, prefixKey, otherArgs...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Get(ctx context.Context, key string) (s string, err error) {
	command := "redisext.Get"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.Get(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) MGet(ctx context.Context, keys ...string) (v []interface{}, err error) {
	command := "redisext.MGet"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		var prefixKey = make([]string, 0, len(keys))
		for _, v := range keys {
			prefixKey = append(prefixKey, m.prefixKey(v))
		}
		v, err = client.MGet(ctx, prefixKey...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Set(ctx context.Context, key string, val interface{}, exp time.Duration) (s string, err error) {
	command := "redisext.Set"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.Set(ctx, m.prefixKey(key), val, exp).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Append(ctx context.Context, key, val string) (n int64, err error) {
	command := "redisext.Append"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.Append(ctx, m.prefixKey(key), val).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) MSet(ctx context.Context, pairs ...interface{}) (s string, err error) {
	command := "redisext.MSet"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		var prefixPairs = make([]interface{}, 0, len(pairs))
		for k, v := range pairs {
			if (k & 1) == 0 {
				prefixPairs = append(prefixPairs, m.prefixKey(v.(string)))
			} else {
				prefixPairs = append(prefixPairs, v)
			}
		}
		s, err = client.MSet(ctx, prefixPairs...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) GetBit(ctx context.Context, key string, offset int64) (n int64, err error) {
	command := "redisext.GetBit"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.GetBit(ctx, m.prefixKey(key), offset).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SetBit(ctx context.Context, key string, offset int64, value int) (n int64, err error) {
	command := "redisext.SetBit"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.SetBit(ctx, m.prefixKey(key), offset, value).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Incr(ctx context.Context, key string) (n int64, err error) {
	command := "redisext.Incr"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.Incr(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) IncrBy(ctx context.Context, key string, val int64) (n int64, err error) {
	command := "redisext.IncrBy"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.IncrBy(ctx, m.prefixKey(key), val).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Decr(ctx context.Context, key string) (n int64, err error) {
	command := "redisext.Decr"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.Decr(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) DecrBy(ctx context.Context, key string, val int64) (n int64, err error) {
	command := "redisext.DecrBy"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.DecrBy(ctx, m.prefixKey(key), val).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SetNX(ctx context.Context, key string, val interface{}, exp time.Duration) (b bool, err error) {
	command := "redisext.SetNX"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		b, err = client.SetNX(ctx, m.prefixKey(key), val, exp).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Exists(ctx context.Context, key string) (n int64, err error) {
	command := "redisext.Exists"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.Exists(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Del(ctx context.Context, key string) (n int64, err error) {
	command := "redisext.Del"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.Del(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Type(ctx context.Context, key string) (s string, err error) {
	command := "redisext.Type"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.Type(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Expire(ctx context.Context, key string, expiration time.Duration) (b bool, err error) {
	command := "redisext.Expire"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		b, err = client.Expire(ctx, m.prefixKey(key), expiration).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

// hashes apis
func (m *RedisExt) HSet(ctx context.Context, key string, field string, value interface{}) (b bool, err error) {
	command := "redisext.HSet"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		b, err = client.HSet(ctx, m.prefixKey(key), field, value).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HDel(ctx context.Context, key string, fields ...string) (n int64, err error) {
	command := "redisext.HDel"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.HDel(ctx, m.prefixKey(key), fields...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HExists(ctx context.Context, key string, field string) (b bool, err error) {
	command := "redisext.HExists"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		b, err = client.HExists(ctx, m.prefixKey(key), field).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HGet(ctx context.Context, key string, field string) (s string, err error) {
	command := "redisext.HGet"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.HGet(ctx, m.prefixKey(key), field).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HGetAll(ctx context.Context, key string) (sm map[string]string, err error) {
	command := "redisext.HGetAll"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		sm, err = client.HGetAll(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HIncrBy(ctx context.Context, key string, field string, incr int64) (n int64, err error) {
	command := "redisext.HIncrBy"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.HIncrBy(ctx, m.prefixKey(key), field, incr).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HIncrByFloat(ctx context.Context, key string, field string, incr float64) (f float64, err error) {
	command := "redisext.HIncrByFloat"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		f, err = client.HIncrByFloat(ctx, m.prefixKey(key), field, incr).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HKeys(ctx context.Context, key string) (ss []string, err error) {
	command := "redisext.HKeys"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		ss, err = client.HKeys(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HLen(ctx context.Context, key string) (n int64, err error) {
	command := "redisext.HLen"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.HLen(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HMGet(ctx context.Context, key string, fields ...string) (vs []interface{}, err error) {
	command := "redisext.HMGet"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		vs, err = client.HMGet(ctx, m.prefixKey(key), fields...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HMSet(ctx context.Context, key string, fields map[string]interface{}) (s string, err error) {
	command := "redisext.HMSet"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.HMSet(ctx, m.prefixKey(key), fields).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HSetNX(ctx context.Context, key string, field string, val interface{}) (b bool, err error) {
	command := "redisext.HSetNX"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		b, err = client.HSetNX(ctx, m.prefixKey(key), field, val).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HVals(ctx context.Context, key string) (ss []string, err error) {
	command := "redisext.HVals"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		ss, err = client.HVals(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

// sorted set apis
func (m *RedisExt) ZAdd(ctx context.Context, key string, members []Z) (n int64, err error) {
	command := "redisext.ZAdd"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZAdd(ctx, m.prefixKey(key), toRedisZSlice(members)...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZAddNX(ctx context.Context, key string, members []Z) (n int64, err error) {
	command := "redisext.ZAddNX"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZAddNX(ctx, m.prefixKey(key), toRedisZSlice(members)...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZAddNXCh(ctx context.Context, key string, members []Z) (n int64, err error) {
	command := "redisext.ZAddNXCh"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZAddNXCh(ctx, m.prefixKey(key), toRedisZSlice(members)...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZAddXX(ctx context.Context, key string, members []Z) (n int64, err error) {
	command := "redisext.ZAddXX"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZAddXX(ctx, m.prefixKey(key), toRedisZSlice(members)...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZAddXXCh(ctx context.Context, key string, members []Z) (n int64, err error) {
	command := "redisext.ZAddXXCh"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZAddXXCh(ctx, m.prefixKey(key), toRedisZSlice(members)...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZAddCh(ctx context.Context, key string, members []Z) (n int64, err error) {
	command := "redisext.ZAddCh"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZAddCh(ctx, m.prefixKey(key), toRedisZSlice(members)...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZCard(ctx context.Context, key string) (n int64, err error) {
	command := "redisext.ZCard"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZCard(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZCount(ctx context.Context, key, min, max string) (n int64, err error) {
	command := "redisext.ZCount"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZCount(ctx, m.prefixKey(key), min, max).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRange(ctx context.Context, key string, start, stop int64) (ss []string, err error) {
	command := "redisext.ZRange"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		ss, err = client.ZRange(ctx, m.prefixKey(key), start, stop).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRangeByLex(ctx context.Context, key string, by ZRangeBy) (ss []string, err error) {
	command := "redisext.ZRangeByLex"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		ss, err = client.ZRangeByLex(ctx, m.prefixKey(key), toRedisZRangeBy(by)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRangeByScore(ctx context.Context, key string, by ZRangeBy) (ss []string, err error) {
	command := "redisext.ZRangeByScore"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		ss, err = client.ZRangeByScore(ctx, m.prefixKey(key), toRedisZRangeBy(by)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRangeByScoreWithScores(ctx context.Context, key string, by ZRangeBy) (zs []Z, err error) {
	command := "redisext.ZRangeByScoreWithScores"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	var rzs []redis2.Z
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		rzs, err = client.ZRangeByScoreWithScores(ctx, m.prefixKey(key), toRedisZRangeBy(by)).Result()
		zs = fromRedisZSlice(rzs)
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRangeWithScores(ctx context.Context, key string, start, stop int64) (zs []Z, err error) {
	command := "redisext.ZRangeWithScores"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	var rzs []redis2.Z
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		rzs, err = client.ZRangeWithScores(ctx, m.prefixKey(key), start, stop).Result()
		zs = fromRedisZSlice(rzs)
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRevRange(ctx context.Context, key string, start, stop int64) (ss []string, err error) {
	command := "redisext.ZRevRange"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		ss, err = client.ZRevRange(ctx, m.prefixKey(key), start, stop).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) (zs []Z, err error) {
	command := "redisext.ZRevRangeWithScores"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	var rzs []redis2.Z
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		rzs, err = client.ZRevRangeWithScores(ctx, m.prefixKey(key), start, stop).Result()
		zs = fromRedisZSlice(rzs)
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRevRangeByScoreWithScores(ctx context.Context, key string, by ZRangeBy) (zs []Z, err error) {
	command := "redisext.ZRevRangeByScoreWithScores"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	var rzs []redis2.Z
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		rzs, err = client.ZRevRangeByScoreWithScores(ctx, m.prefixKey(key), toRedisZRangeBy(by)).Result()
		zs = fromRedisZSlice(rzs)
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRevRangeByScore(ctx context.Context, key string, by ZRangeBy) (ss []string, err error) {
	command := "redisext.ZRevRangeByScore"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		ss, err = client.ZRevRangeByScore(ctx, m.prefixKey(key), toRedisZRangeBy(by)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRank(ctx context.Context, key string, member string) (n int64, err error) {
	command := "redisext.ZRank"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZRank(ctx, m.prefixKey(key), member).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRevRank(ctx context.Context, key string, member string) (n int64, err error) {
	command := "redisext.ZRevRank"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZRevRank(ctx, m.prefixKey(key), member).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRem(ctx context.Context, key string, members []interface{}) (n int64, err error) {
	command := "redisext.ZRem"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		n, err = client.ZRem(ctx, m.prefixKey(key), members).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRemRangeByScore(ctx context.Context, key, min, max string) (i int64, err error) {
	command := "redisext.ZRemRangeByScore"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		i, err = client.ZRemRangeByScore(ctx, m.prefixKey(key), min, max).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZRemRangeByRank(ctx context.Context, key string, start int64, stop int64) (i int64, err error) {
	command := "redisext.ZRemRangeByRank"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		i, err = client.ZRemRangeByRank(ctx, m.prefixKey(key), start, stop).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZIncr(ctx context.Context, key string, member Z) (f float64, err error) {
	command := "redisext.ZIncr"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		f, err = client.ZIncr(ctx, m.prefixKey(key), member.toRedisZ()).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZIncrNX(ctx context.Context, key string, member Z) (f float64, err error) {
	command := "redisext.ZIncrNX"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		f, err = client.ZIncrNX(ctx, m.prefixKey(key), member.toRedisZ()).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZIncrXX(ctx context.Context, key string, member Z) (f float64, err error) {
	command := "redisext.ZIncrXX"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		f, err = client.ZIncrXX(ctx, m.prefixKey(key), member.toRedisZ()).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZIncrBy(ctx context.Context, key string, increment float64, member string) (f float64, err error) {
	command := "redisext.ZIncrBy"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		f, err = client.ZIncrBy(ctx, m.prefixKey(key), increment, member).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZScore(ctx context.Context, key string, member string) (f float64, err error) {
	command := "redisext.ZScore"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		f, err = client.ZScore(ctx, m.prefixKey(key), member).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) TTL(ctx context.Context, key string) (d time.Duration, err error) {
	command := "redisext.TTL"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		d, err = client.TTL(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) Pipeline(ctx context.Context) (pipe *PipelineExt, err error) {
	command := "redisExt.Pipeline"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		p := client.Pipeline()
		pipe = &PipelineExt{namespace: m.namespace, prefix: m.prefix, pipe: p}
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SScan(ctx context.Context, key string, cursor uint64, match string, count int64) (keys []string, rcursor uint64, err error) {
	command := "redisext.SScan"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		keys, rcursor, err = client.SScan(ctx, m.prefixKey(key), cursor, match, count).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SAdd(ctx context.Context, key string, members ...interface{}) (i int64, err error) {
	command := "redisext.SAdd"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		i, err = client.SAdd(ctx, m.prefixKey(key), members...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SPop(ctx context.Context, key string) (s string, err error) {
	command := "redisext.SPop"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.SPop(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SPopN(ctx context.Context, key string, count int64) (s []string, err error) {
	command := "redisext.SPopN"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.SPopN(ctx, m.prefixKey(key), count).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SRem(ctx context.Context, key string, members ...interface{}) (i int64, err error) {
	command := "redisext.SRem"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		i, err = client.SRem(ctx, m.prefixKey(key), members...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SCard(ctx context.Context, key string) (i int64, err error) {
	command := "redisext.SCard"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		i, err = client.SCard(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SIsMember(ctx context.Context, key string, member interface{}) (b bool, err error) {
	command := "redisext.SIsMember"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		b, err = client.SIsMember(ctx, m.prefixKey(key), member).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SMembers(ctx context.Context, key string) (s []string, err error) {
	command := "redisext.SMembers"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.SMembers(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SRandMember(ctx context.Context, key string) (s string, err error) {
	command := "redisext.SRandMember"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.SRandMember(ctx, m.prefixKey(key)).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SRandMemberN(ctx context.Context, key string, count int64) (s []string, err error) {
	command := "redisext.SRandMemberN"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		s, err = client.SRandMemberN(ctx, m.prefixKey(key), count).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) ZScan(ctx context.Context, key string, cursor uint64, match string, count int64) (keys []string, rcursor uint64, err error) {
	command := "redisext.ZScan"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		keys, rcursor, err = client.ZScan(ctx, m.prefixKey(key), cursor, match, count).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) HScan(ctx context.Context, key string, cursor uint64, match string, count int64) (keys []string, rcursor uint64, err error) {
	command := "redisext.HScan"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		keys, rcursor, err = client.HScan(ctx, m.prefixKey(key), cursor, match, count).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) SInter(ctx context.Context, keys ...string) (result []string, err error) {
	command := "redisext.SInter"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		var prefixKey = make([]string, 0, len(keys))
		for _, v := range keys {
			prefixKey = append(prefixKey, m.prefixKey(v))
		}
		result, err = client.SInter(ctx, prefixKey...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) PFAdd(ctx context.Context, key string, els ...interface{}) (result int64, err error) {
	command := "redisext.PFAdd"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		result, err = client.PFAdd(ctx, m.prefixKey(key), els...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) PFMerge(ctx context.Context, dest string, keys ...string) (result string, err error) {
	command := "redisext.PFMerge"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		var prefixKey = make([]string, 0, len(keys))
		for _, v := range keys {
			prefixKey = append(prefixKey, m.prefixKey(v))
		}
		result, err = client.PFMerge(ctx, m.prefixKey(dest), prefixKey...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}

func (m *RedisExt) PFCount(ctx context.Context, keys ...string) (result int64, err error) {
	command := "redisext.PFCount"
	span, ctx := xtrace.StartSpanFromContext(ctx, command)
	st := xtime.NewTimeStat()
	defer func() {
		span.Finish()
		statReqDuration(ctx, m.namespace, command, m.cluster, st.Millisecond())
	}()
	client, err := m.getRedisInstance(ctx)
	if err == nil {
		var prefixKey = make([]string, 0, len(keys))
		for _, v := range keys {
			prefixKey = append(prefixKey, m.prefixKey(v))
		}
		result, err = client.PFCount(ctx, prefixKey...).Result()
	}
	statReqErr(ctx, m.namespace, command, m.cluster, err)
	return
}
